package de.richsource.gwtplug.rebind;

import java.io.PrintWriter;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

import de.richsource.gwtplug.client.PluginComponent;

public class PluginComponentImporterFactoryGenerator extends Generator {

	/**
	 * The name of the generated class.
	 */
	protected String generatedClassName;
	/**
	 * The name of the package to use for the generated class.
	 */
	protected String packageName;

	@Override
	public String generate(TreeLogger logger, GeneratorContext context,
			String typeName) throws UnableToCompleteException {
		TypeOracle typeOracle = context.getTypeOracle();
		try {
			// get the type that was used in the GWT.create call.
			// this is the super class or interface for the generated class.
			JClassType superType = typeOracle.getType(typeName);

			// Generate the class if it doesn't exist yet
			return generateForSuperClass(logger, context, superType);
		} catch (Exception e) {
			logger.log(Type.ERROR, e.getMessage());
			throw new UnableToCompleteException();
		}

	}

	/**
	 * Calculates the name of the generated class.
	 * 
	 * @param context
	 *            the GeneratorContext.
	 * @param logger
	 *            the logger to use.
	 * @param simpleSourceName
	 *            the simple source name of the supertype
	 * @return the name of the class to be generated
	 */
	protected String getGeneratedClassName(GeneratorContext context,
			TreeLogger logger, String simpleSourceName) {
		return simpleSourceName + "Impl";
	}

	private String generateForSuperClass(TreeLogger logger,
			GeneratorContext context, JClassType superType) {
		packageName = superType.getPackage().getName();
		generatedClassName = getGeneratedClassName(context, logger,
				superType.getSimpleSourceName());
		String qualifiedClassName = packageName + "." + generatedClassName;

		PrintWriter printWriter = context.tryCreate(logger, packageName,
				generatedClassName);

		if (printWriter == null) {
			// the generated type already exists
			return qualifiedClassName;
		}

		generateNewClass(logger, context, superType, printWriter);
		return qualifiedClassName;
	}

	/**
	 * Generates the new class using the given super type (super class or
	 * interface).
	 * 
	 * @param logger
	 *            the logger to use.
	 * @param context
	 *            the GeneratorContext to use.
	 * @param superType
	 *            the super type to generate a concrete implemetation for.
	 * @param printWriter
	 *            the {@link PrintWriter} to generate the contents with.
	 */
	private void generateNewClass(TreeLogger logger, GeneratorContext context,
			JClassType superType, PrintWriter printWriter) {

		TypeOracle typeOracle = context.getTypeOracle();
		JClassType pluginComponentType;
		JClassType[] subtypes;
		try {
			pluginComponentType = typeOracle.getType(PluginComponent.class.getName());
			subtypes = pluginComponentType
					.getSubtypes();

		} catch (NotFoundException e) {
			throw new IllegalStateException();
		}

		ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(
				packageName, generatedClassName);
		composer.setSuperclass(superType.isClassOrInterface()
				.getQualifiedSourceName());

		composer.addImport(JavaScriptObject.class.getName());

		SourceWriter sourceWriter = composer.createSourceWriter(context,
				printWriter);

		sourceWriter
				.println("public Object createForType(String type, JavaScriptObject component) {");

		for (JClassType jClassType : subtypes) {
			
			JParameterizedType parameterizedType = jClassType.getImplementedInterfaces()[0].isParameterized();
			JClassType t = parameterizedType.getTypeArgs()[0];
			String generated = new PluginComponentImporterGenerator().generateForSuperClass(
					logger, context, t);

			sourceWriter.println("if(\""+t.getQualifiedSourceName()+"\".equals(type)) {");
			sourceWriter
					.println("return new "+generated+"(component);");
			sourceWriter.println("}");
		}

		sourceWriter.println("return null;");
		sourceWriter.println("}");

		sourceWriter.commit(logger);
	}

}
